
//bind all the functions of this object to itself.
function $I(object){
	for(var k in object)
		if(typeof object[k] == 'function' && !(object instanceof object[k]))
			object[k] = object[k].bind(object);
}

//declaration a strong class
function $C(declaration){
	var clazz = Object.extend(
		function(){
			$I(this);
			this.initialize.apply(this, arguments);
		},
		declaration || {});
	clazz.prototype.clazz = clazz;
	return clazz;
}

function $apply(method){
	return function(obj, args){
		return method.apply(obj, args || []);
	}
}

function $call(method){
	return function(){
		var args = $A(arguments), obj = args.shift();
		return method.apply(obj, args);
	}
}

//get a constructor of a class, (option: using following arguments can bind the parameters to constructor, undefined one is for skipping),
function $ctor(clazz){
	var args = $A(arguments);
	args.shift();
	return function(){ 
		var a = args.clone(), b = $A(arguments);
		a.each(function(it,i){
			if(it === undefined)
				a[i] = b.shift();
		});
		b.each(a.push.bind(a));
		var s = 'return new this(';
		if(a.length>0){
			s += 'arguments[0]';
			for(var i=1; i<a.length; i++)
				s +=',arguments['+i+']';
		}
		s += ');';
		s = new Function("", s);
		return s.apply(clazz, a);
	};
}

Runner = {
	through: function(methods){
		if(!(methods instanceof Array))
			methods = $A(arguments);
		return function(obj){
			methods.each(function(v){obj=v(obj);});
			return obj;
		};
	},
	all: function(methods){
		if(!(methods instanceof Array))
			methods = $A(arguments);
		return function(){
			var a = [], args = $A(arguments);
			methods.each(function(v){a.push(v.apply(null,args));});
			return a;
		};
	},
	doreturn:function(v){
		return function(){return v;};
	}
}

var Parser = $C({
	prototype:{
		initialize: function(regexp, option, items) {
			this.regexp = regexp;
			option = option || '';
			this.global = option.indexOf('g')>=0;
			this.option = (option.indexOf('m')>=0?'m':'')+(option.indexOf('i')>=0?'i':'');
			this.regExp4First = new RegExp(regexp, this.option);
			this.regExp4All = new RegExp(regexp, 'g'+this.option);
			this.items = {};
			for(var k in items){
				var a = items[k];
				if(a == null)
					a = [null, null];
				else if(typeof(a) == 'number')
					a = [a, null];
				else if(typeof(a) == 'function')
					a = [null, a];
				this.items[k] = a;
			};
		},

		parseFirst: function(text){
			var a = text.match(this.regExp4First);
			if(a == null)
				return null;
			var r = {};
			for(var k in this.items){
				var i = this.items[k];
				var b = i[0] != null ? a[i[0]] : null;
				r[k] = i[1] != null ? i[1](b) : b;
			}
			return r;
		},

		parseAll: function(text){
			var a = text.match(this.regExp4All);
			if(a == null)
				return null;
			for(var i=0; i<a.length; i++)
				a[i] = this.parseFirst(a[i]);
			return a;
		},

		parse: function(text){
			return this.global ? this.parseAll(text) : this.parseFirst(text);
		}
	}
});

var URL = $C({
	prototype: {
		initialize: function(str){
			var a = str.indexOf('://');
			if(a<0){
				str = 'http://' + str;
				a = 4;
			}
			this.protocol = str.substr(0, a);
			a += 3;
			var b = str.indexOf('/', a);
			if(b<0){
				b = str.length;
				str += '/';
			}

			this.path = str.substr(b);
			str = str.substr(a, b - a);
			a = str.indexOf(':');
			if(a>=0){
				this.port = parseInt(str.substr(a+1));
				str = str.substr(0, a);
			}
			this.domain = str.toLowerCase();
		},
		
		toString: function(){
			return this.protocol + '://' + this.domain + (this.port ? (':' + this.port) : '') + this.path;
		}
	}
});

Object.extend(String.prototype, {
	toCodeArray: function() {
		var a = new Array(this.length);
		for(var i=0; i<this.length; i++)
			a[i] = this.charCodeAt(i);
		return a;
	}
});


var ST = $C({
	prototype:{
		initialize: function(numberLimit, initData){
			this.numberLimit = numberLimit;
			initData = initData || [];
			if(initData.length > numberLimit)
				initData.splice(numberLimit);
			this.data = initData;
			var a = 0;
			initData.each(function(it){a += it;});
			this.value = a / numberLimit;
		},

		add: function(v){
			if(this.data.length == this.numberLimit){
				var a = this.data.shift();
				this.value += (v - a) / this.numberLimit;
				this.data.push(v);
			}
			else if(v>this.value*10)
				return false;
			else{
				this.value = (this.value * this.data.length + v) / this.data.length;
				this.data.push(v);
			}
			return true;
		}
	}
});